package com.hazardousholdings.unkj.validation;

import java.util.ArrayList;
import java.util.List;

/**
 * ParsingCompositeValidator is a CompositeValidator that starts with a String.
 * String based validators can be added to do validation before the parsing.
 * Target type validators can be added to do validation after parsing.
 * A ParsingValidator must be specified to do the parsing between String and target type.
 */
public class ParsingCompositeValidator<T> extends CompositeValidator<T> implements ParsingValidator<T> {

	protected CompositeValidator<String> preParseValidator;
	protected ParsingValidator<T> parsingValidator;
	
	/**
	 * Instantiates a new parsing composite validator.
	 * The parsingValidator will be invoked after any preParseValidators and 
	 * its result will be used for subsequent target type validators.
	 * 
	 * Invalid messages from the underlying validators will be used as the result message.
	 * 
	 * @param parsingValidator the parsing validator
	 */
	public ParsingCompositeValidator(ParsingValidator<T> parsingValidator) {
		this(null, parsingValidator);
	}
	
	/**
	 * Instantiates a new parsing composite validator.
	 * The parsingValidator will be invoked after any preParseValidators and 
	 * its result will be used for subsequent target type validators.
	 *  
	 * @param invalidMessage the invalid message to use instead of the underlying validator messages
	 * @param parsingValidator the parsing validator
	 */
	public ParsingCompositeValidator(String invalidMessage, ParsingValidator<T> parsingValidator) {
		super(invalidMessage);
		this.parsingValidator = parsingValidator;
		this.preParseValidator = new CompositeValidator<String>();
	}

	/**
	 * String validators to be checked before the ParsingValidator
	 * 
	 * @param validator validator to be added to the list
	 */
	public void addPreParseValidator(Validator<String> validator) {
		if(validator != null) {
			this.preParseValidator.addValidator(validator);
		}
	}

	@Override
	public ValidationResults<T> validate(String input) {
		boolean compositeValid = true;
		T compositeResult = null;
		List<String> validationErrors = new ArrayList<String>(this.validators.size());
		
		ValidationResults<String> preParseResult = this.preParseValidator.validate(input);
		if(preParseResult.isValid()) {
			
			ValidationResults<T> parseResult = this.parsingValidator.validate(input);
			if(parseResult.isValid()) {
				
				ValidationResults<T> postParseResult = this.validate(parseResult.getResult());
				if(!postParseResult.isValid()) {
					compositeValid = false;
					validationErrors.addAll(postParseResult.getMessages());
				}
				
			} else {
				compositeValid = false;
				validationErrors.addAll(parseResult.getMessages());
			}
			
		} else {
			compositeValid = false;
			validationErrors.addAll(preParseResult.getMessages());
		}
		
		return new ValidationResults<T>(compositeResult, compositeValid, validationErrors);
	}

}
